#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>

#define PIPE_ERROR(pipefd, errorValue)  \
            if (pipe(pipefd)==errorValue) { \
                perror("创建管道失败\n"); \
                exit(EXIT_FAILURE); \
            }

// ./main 123456 执行程序
int main(int argc, char const *argv[]) {
    int pipefd[2];
    pid_t cpid;
    char buf;
    if (argc != 2) {
        fprintf(stderr, "%s:请填写需要传递的信息\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    // step 1# 创建一个管道
    if (pipe(pipefd) == -1) {
        perror("创建管道失败\n");
        exit(EXIT_FAILURE);
    }
    // step 2# 创建子进程 注意：父子进程各自都有一份指向同一个内核缓冲区的管道读端和写端
    // 父子进程各自有自己的读端和写端
    cpid = fork();
    // note 1# 可以用带参宏定义简化代码
    // if (cpid == -1) {
    // PIPE_ERROR(pipefd, -1)
    // }
    if (cpid == -1) {
        perror("创建子进程失败\n");
        exit(EXIT_FAILURE);
    }
    if (cpid == 0) {
        // step 3# 子进程关闭写端，表示子进程在读管道
        close(pipefd[1]);
        char str[100] = {0};
        sprintf(str, "子进程%d尝试从管道读取数据...\n", getpid());
        write(STDOUT_FILENO, str, strlen(str));
        // step 5# 如果管道中没有数据或者父进程的写端没有关闭，则子进程的 read 会阻塞
        while (read(pipefd[0], &buf, 1) > 0) {
            write(STDOUT_FILENO, &buf, 1);
        }
        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        _exit(EXIT_SUCCESS); // 子进程退出不释放资源
    }
    else {
        // step 4# 父进程比子进程有更高优先级，所以一般父进程先执行
        sleep(1);
        close(pipefd[0]); // 关闭父进程读端 注意：不会影响子进程的读端
        printf("父进程%d向管道写入数据...\n", getpid());
        write(pipefd[1], argv[1], strlen(argv[1])); // 开启父进程写端：父进程写端开启时，子进程读端会阻塞
        close(pipefd[1]);
        waitpid(cpid, NULL, 0);
        exit(EXIT_SUCCESS);
    }
}